package com.huatai.bi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huatai.bi.bo.DataSetColumnBO;
import com.huatai.bi.bo.DataSetColumnTypeBO;
import com.huatai.bi.constant.RedisConstant;
import com.huatai.bi.constant.SystemConstant;
import com.huatai.bi.dto.*;
import com.huatai.bi.entity.*;
import com.huatai.bi.enums.ProcessEnum;
import com.huatai.bi.mapper.BigScreenImageMapper;
import com.huatai.bi.mapper.DataProcessInfoMapper;
import com.huatai.bi.mapper.DataProcessMapper;
import com.huatai.bi.service.*;
import com.huatai.bi.utils.FilterComponent.Node;
import com.huatai.bi.vo.ApiConfigVO;
import com.huatai.bi.vo.DataProcessVO;
import com.huatai.bi.vo.Direcr.PreviewDatVO;
import com.huatai.bi.vo.ProcessDataSetVO;
import com.huatai.bi.vo.ProcessDataVO;
import com.huatai.bi.vo.bigScreenVO.ProDirDataSetVO;
import com.huatai.bi.vo.excelVo.HeardVO;
import com.huatai.common.error.exception.ServiceException;
import com.huatai.common.util.AuthUtil;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author hecai
 * @description 针对表【tb_data_process(数据集加工描述)】的数据库操作Service实现
 * @createDate 2024-06-27 18:33:47
 */
@Service
public class DataProcessServiceImpl extends ServiceImpl<DataProcessMapper, DataProcessEntity> implements DataProcessService {
    @Autowired
    private ClickHouseJDBCService clickHouseJDBCService;
    @Autowired
    private DirectoryService directoryService;
    @Autowired
    private DataProcessInfoService dataProcessInfoService;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    DataProcessInfoMapper dataProcessInfoMapper;
    @Autowired
    DataSetColumnService dataSetColumnService;
    @Autowired
    private BigScreenImageMapper bigScreenImageMapper;

    /**
     * 获取指标数据项
     * @param dtId
     * @return
     */
    @Override
    public List<DataSetColumnBO> getDataSetColumnByAggregate(Long dtId){
        return baseMapper.getDataSetColumnByAggregate(dtId);
    }

    /**
     * 修改分页大小
     * @param dpId
     * @param pageSize
     * @return
     */
    public Boolean updatePageSize(Long dpId,Integer pageSize){
        DataProcessEntity entity = this.getById(dpId);
        if(ObjectUtil.isEmpty(entity)){
            throw new RuntimeException("加工数据集不存在，请确认标识!!!");
        }
        entity.setPageSize(pageSize);
        return this.updateById(entity);
    }

    /**
     * 获取接口配置信息
     * @param dpId
     * @return
     */
    public ApiConfigVO getApiConfig(Long dpId){
        DataProcessEntity entity = this.getById(dpId);
        if(ObjectUtil.isEmpty(entity)){
            throw new RuntimeException("数据加工标识不存在!!!请确认加工标识数据");
        }
        return BeanUtil.copyProperties(entity,ApiConfigVO.class);
    }

    /**
     * 加工数据集数据预览
     *
     * @param dtId      加工数据集id
     * @param pageIndex 分页页码
     * @param pageSize
     * @return
     */
    @Override
    public PreviewDatVO previewProcessData(Long dtId, Integer pageIndex, Integer pageSize) {
        DataProcessEntity entity = this.getById(dtId);
        if (ObjectUtil.isEmpty(entity)) {
            throw new ServiceException("加工数据集不存在");
        }
		List<LinkedHashMap<String, Object>> hashMaps = new LinkedList<>();
		if(StringUtils.equals("aggregate",entity.getType())){
			hashMaps = this.clickHouseJDBCService.queryTableByProcess(entity);
		}else{
			hashMaps = this.clickHouseJDBCService.queryTableByTable(entity.getTableName(), pageIndex, pageSize, " ");
		}

        List<HeardVO> heardList = dataSetColumnService.heardListById(dtId);
        PreviewDatVO previewDatVO = new PreviewDatVO();
        previewDatVO.setHeardVos(heardList);
        previewDatVO.setBodyVos(hashMaps);
        previewDatVO.setTotalSize(clickHouseJDBCService.getTableSizeTableName(entity.getTableName()));
        return previewDatVO;
    }

    /**
     * 分页获取数据
     *
     * @param dtId
     * @param pageIndex
     * @param pageSize
     * @return
     */
    public IPage<DataProcessVO> getPageData(Long dtId, Integer pageIndex, Integer pageSize) {
        IPage<DataProcessVO> pageData = new Page<>();
        if(ObjectUtil.isNotEmpty(dtId)){
            pageData = this.baseMapper.getPageData(new Page<DataProcessVO>(pageIndex<1?1:pageIndex, pageSize<1?10:pageSize), dtId);
        }
        return pageData;
    }

    /**
     * 删除数据加工操作
     *
     * @param id
     * @return
     */
    public Boolean removeById(Long id) {
        return super.removeById(id);
    }

    @Override
    public Boolean saveDataProcess(DataProcessDto dataProcessDto) {
        LambdaQueryWrapper<DataProcessInfoEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DataProcessInfoEntity::getDpId, dataProcessDto.getDpId());
        DataProcessInfoEntity dataProcessInfo = dataProcessInfoService.list(wrapper)
                .stream().max(Comparator.comparing(DataProcessInfoEntity::getOrderNum)).get();
        DataProcessEntity dataProcessEntity = this.getById(dataProcessDto.getDpId());
        dataProcessEntity.setName(dataProcessDto.getName());
        dataProcessEntity.setDescription(dataProcessDto.getDescription());
        dataProcessEntity.setTableName(dataProcessInfo.getProcessTable());
        dataProcessEntity.setFullSql(dataProcessInfo.getParameterValues());
        return this.updateById(dataProcessEntity);
    }

    @Override
    public Long getDataProcessId(Long dtId) {
        DataProcessEntity dataProcessEntity = new DataProcessEntity();
        dataProcessEntity.setDtId(dtId);
        if (this.save(dataProcessEntity)) {
            return dataProcessEntity.getId();
        } else {
            throw new ServiceException("获取加工数据集失败！");
        }
    }
    @Override
    public DataProcessInitDto getId(Long dtId) {

        DataProcessInitDto dto = new DataProcessInitDto();
        //根据用户和数据集ID生成缓存key,缓存数据加工ID
        String redisKey = RedisConstant.DATA_SET_PROCESS+ AuthUtil.getUser().getUserId() +":";
        String dtKey = redisKey+dtId;
        String dpKey = null;
        Long dpId = null;
        List<DataProcessInfoDto> list = new ArrayList<>();
        //已经存在
        if(redissonClient.getBucket(dtKey).isExists()){
            dpId = (Long) redissonClient.getBucket(dtKey).get();
            dpKey = redisKey+dpId;

            list.addAll(getInfoList(dpId));
            redissonClient.getBucket(dtKey).delete();
            redissonClient.getBucket(dpKey).delete();
        }
        //数据加工ID不存在,生成
        if(null == dpId){
            while (true){
                dpId = IdWorker.getId();
                //判断ID在库里是否已经存在,存在重新生成
                if(null == getById(dpId)){
                    break;
                }
            }
            dpKey = redisKey+dpId;
            List<DataSetColumnEntity> columnList = dataSetColumnService.list(Wrappers.lambdaQuery(DataSetColumnEntity.class).eq(DataSetColumnEntity::getDataSetId,dtId));
            for (int i = 0; i < columnList.size(); i++) {
                DataSetColumnEntity column  = columnList.get(i);
                column.setId(null);
                column.setDataSetId(dpId);
                column.setCreateTime(null);
                column.setUpdateTime(null);
            }
            dataSetColumnService.saveBatch(columnList);
        }

        //将数据集ID和数据加工ID相互缓存
        redissonClient.getBucket(dtKey).set(dpId,24, TimeUnit.HOURS);
        redissonClient.getBucket(dpKey).set(dtId,24, TimeUnit.HOURS);
        dto.setDpId(dpId);
        dto.setList(list);
        return dto;
    }

    @Override
    public DataProcessDto get(Long dpId) {

        DataProcessDto dto = new DataProcessDto();

        DataProcessEntity process = getById(dpId);
        if(null == process){
            throw new ServiceException("记录不存在");
        }

        dto.setDpId(dpId);
        dto.setName(process.getName());
        dto.setDescription(process.getDescription());
        dto.setList(getInfoList(dpId));
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveProcess(DataProcessDto dto) {

        DataProcessEntity process = getById(dto.getDpId());
        if(null == process){
            List<DataProcessInfoEntity> infoList = dataProcessInfoService.list(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId,dto.getDpId()));
            if(CollectionUtils.isEmpty(infoList)){
                throw new ServiceException("没有加工数据，请检查");
            }
            //获取第一次和最后一次加工的数据
            DataProcessInfoEntity first = infoList.stream().min(Comparator.comparing(DataProcessInfoEntity::getOrderNum)).get();
            DataProcessInfoEntity latest = infoList.stream().max(Comparator.comparing(DataProcessInfoEntity::getOrderNum)).get();
            //从缓存获取数据集ID
            String redisKey = RedisConstant.DATA_SET_PROCESS+ AuthUtil.getUser().getUserId()+":";
            String dpKey = redisKey+dto.getDpId();
            if(!redissonClient.getBucket(dpKey).isExists()){
                throw new ServiceException("ID不存在");
            }
            Long dtId = (Long) redissonClient.getBucket(dpKey).get();
            checkName(dtId,dto.getName(),null,SystemConstant.PROCESS_TYPE_PROCESS);


            DirectoryEntity directory = directoryService.getById(dtId);
            if (null == directory) {
                throw new ServiceException("数据集不存在");
            }

            String tableName = directory.getTableName();

            String dtKey = redisKey+dtId;
            DataProcessEntity dataProcessEntity = new DataProcessEntity();
            dataProcessEntity.setId(dto.getDpId());
            dataProcessEntity.setDtId(dtId);
            dataProcessEntity.setName(dto.getName());
            dataProcessEntity.setType(SystemConstant.PROCESS_TYPE_PROCESS);
            dataProcessEntity.setDescription(dto.getDescription());
            dataProcessEntity.setTableName(latest.getProcessTable());
            dataProcessEntity.setFullSql(latest.getParameterValues());
            dataProcessEntity.setVersion(dataProcessEntity.getTableName()+"1");
            save(dataProcessEntity);

            String sql = latest.getParameterValues();
            if(ProcessEnum.UNION.getType().equals(latest.getType())){
                DirectoryEntity unionDataSet = directoryService.getById(latest.getUnionDtId());
                if (null == unionDataSet) {
                    throw new ServiceException("数据集不存在");
                }
                sql = sql.replace("${unionTableName}",unionDataSet.getTableName());
            }

            clickHouseJDBCService.createTableProcessDS(dataProcessEntity.getVersion(), dto.getDpId(), sql);

            //第一条加工数据表名处理为占位符
            first.setParameterValues(first.getParameterValues().replace(tableName,"${tableName}"));
            dataProcessInfoService.updateById(first);

            //删除缓存
            redissonClient.getBucket(dpKey).delete();
            redissonClient.getBucket(dtKey).delete();
        }else{
            if(!process.getName().equals(dto.getName())){
                checkName(process.getDtId(),dto.getName(),process.getId(),SystemConstant.PROCESS_TYPE_PROCESS);
            }
            process.setName(dto.getName());
            process.setDescription(dto.getDescription());
            updateById(process);
        }
    }

    public void cancel(Long id){
        String redisKey = RedisConstant.DATA_SET_PROCESS+ AuthUtil.getUser().getUserId()+":";
        String dpKey = redisKey+id;
        if(redissonClient.getBucket(dpKey).isExists()){
            Long dtId = (Long) redissonClient.getBucket(dpKey).get();
            List<DataProcessInfoEntity> infoList = dataProcessInfoService.list(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId,id));
            if(infoList.size() > 0){
                infoList.stream().forEach(item->{
                    try {
                        clickHouseJDBCService.dropTable(item.getProcessTable());
                    }catch (Exception e){}
                });

            }
            dataProcessInfoService.remove(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId,id));
            dataSetColumnService.remove(Wrappers.lambdaQuery(DataSetColumnEntity.class).eq(DataSetColumnEntity::getDataSetId,id));
            if(null != dtId){
                redissonClient.getBucket(redisKey+dtId).delete();
            }
            redissonClient.getBucket(dpKey).delete();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void savePolymeric(PolymericDto dto){
        DataProcessEntity old = null;
        Long infoId = null;
        if(null != dto.getId()){
            old = getById(dto.getId());
            if(null == old ){
                throw new ServiceException("记录不存在");
            }
            DataProcessInfoEntity info = dataProcessInfoService.getOne(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId, old.getId()));
            if(null != info){
                infoId = info.getId();
            }
        }

        String tableName = null;
        ProDirDataSetVO vo = bigScreenImageMapper.getProDirDataSetById(dto.getDtId());
        if(null == vo || StringUtils.isBlank(vo.getTableName())){
            throw new ServiceException("数据集或数据加工不存在");
        }else{
            tableName = vo.getTableName();
        }

        checkName(dto.getDtId(),dto.getName(),dto.getId(),SystemConstant.PROCESS_TYPE_AGGREGATE);

        DataProcessEntity process = new DataProcessEntity();
        process.setId(dto.getId());
        process.setName(dto.getName());
        process.setDescription(dto.getName());
        process.setDtId(dto.getDtId());
        process.setType(SystemConstant.PROCESS_TYPE_AGGREGATE);
        process.setFullSql(dto.getExpresse());
		process.setTableName(tableName);
        DataSetColumnEntity column  = new DataSetColumnEntity();
        //查询新字段数据类型
        String querySql = "select ("+dto.getExpresse()+") from "+tableName+" limit 0,1";
        List<DataSetColumnTypeBO> caDatalist = clickHouseJDBCService.getColumnListBySql(querySql);
        if(caDatalist.size()==1){
            column.setColumnType(caDatalist.get(0).getColumnType());
        }
        if(SystemConstant.DATA_TYPE_STRING.equals(column.getColumnType())){
            throw new ServiceException("汇总列只能是数值");
        }

		String columnName = SystemConstant.PROCESS_TYPE_AGGREGATE+"_"+uuid(8);

        column.setColumnName(columnName);
        column.setColumnComment(dto.getName());

//		process.setFullSql(process.getFullSql() + " as " + columnName);

        DataProcessInfoEntity info = new DataProcessInfoEntity();
        info.setId(infoId);
        ProcessEnum processEnum = ProcessEnum.POLYMERIC;
        info.setName(processEnum.getName());
        info.setType(processEnum.getType());
        JSONObject param = new JSONObject();
        param.put("filter",dto.getNode());
        param.put("columnDescription",dto.getName());
        param.put("expresse",dto.getExpresse());
        info.setOriginalParameter(param.toJSONString());
        info.setParameterValues(dto.getExpresse());
        info.setOrderNum(1);

        List<DataSetColumnEntity> list = dataSetColumnService.list(Wrappers.lambdaQuery(DataSetColumnEntity.class).eq(DataSetColumnEntity::getDataSetId, dto.getDtId()));
        List<String> expColumns = Arrays.asList(dto.getExpresse().split("`"));
        List<String> useColumn = list.stream().map(item -> item.getColumnName()).filter(item -> expColumns.contains(item)).collect(Collectors.toList());

        info.setUserField(String.join("、",useColumn));

        saveOrUpdate(process);

        info.setDpId(process.getId());
        dataProcessInfoService.saveOrUpdate(info);


        column.setColumnPosition(1);
        column.setDataSetId(process.getId());
        column.setStepNum(1);

        if(null != dto.getId()){
            LambdaUpdateWrapper<DataSetColumnEntity> uw = Wrappers.lambdaUpdate();
            uw.set(DataSetColumnEntity::getColumnComment,dto.getName());
            uw.eq(DataSetColumnEntity::getDataSetId,dto.getId());
            dataSetColumnService.update(uw);
        }else{
            dataSetColumnService.save(column);
        }
    }

    //校验名称是否存在
    private void checkName(Long dtId,String name,Long id,String type){
        LambdaQueryWrapper<DataProcessEntity> qw = Wrappers.lambdaQuery();
        qw.eq(DataProcessEntity::getDtId,dtId);
        qw.eq(DataProcessEntity::getType,type);
        qw.eq(DataProcessEntity::getName,name);
        DataProcessEntity process = getOne(qw);
        if(null != process){
            if(null != id && id.equals(process.getId())){
                return;
            }
            throw new ServiceException("名称已存在,请检查");
        }
    }

    @Override
   public DataProcessListDto listByDataSetId(Long dtId){
        DataProcessListDto dto = new DataProcessListDto();
        List<DataProcessEntity> list = list(Wrappers.<DataProcessEntity>lambdaQuery().eq(DataProcessEntity::getDtId,dtId));
        list.stream().forEach(item -> {
            DataSetListDto dataset = new DataSetListDto();
            dataset.setId(item.getId());
            dataset.setName(item.getName());
            dto.add(item.getType(),dataset);
        });
        return dto;
    }

    public void modifyName(DataProcessEntity entity){
        if(null == entity.getId()){
            throw new ServiceException("记录不存在");
        }
        if(StringUtils.isBlank(entity.getName())){
            return;
        }
        DataProcessEntity old = getById(entity.getId());
        if(null == old){
            throw new ServiceException("记录不存在");
        }
        old.setName(entity.getName());
        updateById(old);
    }

    private List<DataProcessInfoDto> getInfoList(Long dpId){
        List<DataProcessInfoDto> list = new ArrayList<>();
        List<DataProcessInfoEntity> ListInfo = dataProcessInfoService.list(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId,dpId));
        ListInfo.stream().sorted(Comparator.comparing(DataProcessInfoEntity::getOrderNum)).forEach(item -> {
            DataProcessInfoDto infoDto = new DataProcessInfoDto();
            infoDto.setId(item.getId());
            infoDto.setName(item.getName());
            infoDto.setDpId(item.getDpId());
            infoDto.setType(item.getType());
            infoDto.setParam(item.getOriginalParameter());
            list.add(infoDto);
        });
        return list;
    }

    @Override
    public PolymericDto polymericInfoById(Long id){
        PolymericDto dto = new PolymericDto();
        DataProcessEntity process = getById(id);
        if(null == process){
            throw new ServiceException("记录不存在");
        }
        dto.setId(id);
        dto.setDtId(process.getDtId());
        dto.setName(process.getName());

        DataProcessInfoEntity info = dataProcessInfoService.getOne(Wrappers.lambdaQuery(DataProcessInfoEntity.class).eq(DataProcessInfoEntity::getDpId,id));
        JSONObject param = JSONObject.parseObject(info.getOriginalParameter());
        dto.setExpresse(param.getString("expresse"));
        dto.setNode(param.getObject("filter", Node.class));
        dto.setChooseColumn(info.getUserField());

        return dto;
    }

    /**
     * 获取数据集数据加工列表
     * @param dtId
     */
    public List<ProcessDataVO> getProcessDataList(Long dtId){
        List<ProcessDataVO> dataVOS = new ArrayList<>();
        LambdaQueryWrapper<DataProcessEntity> qw = Wrappers.<DataProcessEntity>lambdaQuery();
        qw.isNotNull(DataProcessEntity::getTableName);
        qw.eq(DataProcessEntity::getIsDeleted,0);
        qw.eq(ObjectUtil.isNotEmpty(dtId), DataProcessEntity::getDtId, dtId);
        qw.eq(DataProcessEntity::getType, "process");
        List<DataProcessEntity> list = this.list(qw);
        List<String> collect = list.stream().map(p -> p.getType()).distinct().collect(Collectors.toList());
        for (String type : collect) {
            ProcessDataVO dataVO = new ProcessDataVO();
            dataVO.setTypeName(type.equalsIgnoreCase("process")?"数据加工":"聚合指标");
            List<ProcessDataSetVO> dataSetList = new ArrayList<>();
            List<DataProcessEntity> dataProcessEntities = list.stream().filter(p -> p.getType().equals(type)).collect(Collectors.toList());
            if (dataProcessEntities.size() > 0) {
                dataProcessEntities.forEach(item->{
                    dataSetList.add(new ProcessDataSetVO(item.getId(),item.getName()));
                });
            }
            dataVO.setDataSetList(dataSetList);
            dataVOS.add(dataVO);
        }
        return dataVOS;
    }

    private String uuid(int len){
        String charSet = "abcdefghijklmnopqrstuvwxyz";
        SecureRandom secureRandom = new SecureRandom();
        String randomString = secureRandom.ints(len, 0, charSet.length())
                .mapToObj(charSet::charAt)
                .map(Object::toString)
                .collect(Collectors.joining());

        return randomString;

    }
}




